package org.smog.core.config.security.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.AllArgsConstructor;
import org.smog.core.config.security.model.AuthUser;
import org.smog.core.config.security.model.IAuthUser;
import org.smog.core.config.security.model.JwtToken;
import org.smog.core.config.security.model.JwtUser;
import org.smog.core.exception.NotLoginException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.function.Function;

/**
 * @Description: Created by IntelliJ IDEA.
 * @project_name: smogTemp
 * @time: 2020-05-14 11:22
 * @email: 17685306043@163.com
 * @author: huangZhongYao
 */
@Component
@AllArgsConstructor
public class JwtTokenUtil {

    @Autowired
    JwtProperties jwtProperties;

    public JwtTokenUtil() {
    }

    public JwtToken generateToken(JwtUser user) {
        return this.doGenerateToken(user);
    }

    public String getUsernameFromToken(String token) {
        return this.getClaimFromToken(token, (p) -> {
            return p.get("userName").toString();
        });
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        Claims claims = this.getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        try {
            return Jwts.parser().setSigningKey(this.jwtProperties.getTokenSecret()).parseClaimsJws(token).getBody();
        } catch (Exception var3) {
            var3.printStackTrace();
            throw new NotLoginException("NotLogin");
        }
    }

    public Boolean validateToken(String token) {
        return getAllClaimsFromToken(token) == null;
    }

    public Boolean isTokenExpired(String token) {
        Date expiration = this.getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    public Date getExpirationDateFromToken(String token) {
        return (Date) this.getClaimFromToken(token, Claims::getExpiration);
    }

    public Collection<? extends GrantedAuthority> getAuthorities(String token) {
        Claims claims = this.getAllClaimsFromToken(token);
        List<LinkedHashMap> rolePermissions = (List) claims.get("rolePermissions", List.class);
        Set<String> dbAuthSet = new HashSet();
        if (rolePermissions.size() > 0) {
            rolePermissions.forEach((rolePermission) -> {
                dbAuthSet.add(rolePermission.get("authority").toString());
            });
        }

        return AuthorityUtils.createAuthorityList(dbAuthSet.toArray(new String[0]));
    }

    private JwtToken doGenerateToken(JwtUser user) {
        Claims claims = Jwts.claims();
        claims.put("id", user.getId());
        claims.put("userName", user.getUsername());
        claims.put("realName", user.getRealName());
        claims.put("deviceId", user.getDeviceId());
        claims.put("rolePermissions", user.getAuthorities());
        Date expiration = new Date(System.currentTimeMillis() + this.jwtProperties.getExpiration() * 1000L);
        Date issuedAt = new Date(System.currentTimeMillis());

        JwtToken jwtToken = new JwtToken();
        jwtToken.setTokenType(this.jwtProperties.getTokenPrefix());
        jwtToken.setExpirationDate(expiration);

        JwtBuilder jwtBuilder = Jwts.builder().setClaims(claims)
                .setIssuer(this.jwtProperties.getTokenSecret())
                .setIssuedAt(issuedAt)
                .signWith(SignatureAlgorithm.HS256, this.jwtProperties.getTokenSecret());

        if (this.jwtProperties.isExpirationVerificationEnable()) {
            jwtBuilder.setExpiration(expiration);
            jwtToken.setExpirationDate(expiration);
        }

        String token = jwtBuilder.compact();

        jwtToken.setAccessToken(token);
        return jwtToken;
    }

    public IAuthUser toAuthUser(String token) {
        Claims claims = this.getAllClaimsFromToken(token);

        List<LinkedHashMap> rolePermissions = (List) claims.get("rolePermissions", List.class);

        AuthUser authUser = new AuthUser();
        authUser.setId(String.valueOf(claims.get("id", String.class)));
        authUser.setUserName(claims.get("userName", String.class));
        authUser.setRealName(claims.get("realName", String.class));
        authUser.setDeviceId(claims.get("deviceId", String.class));

        authUser.setRoles(rolePermissions.stream().filter((p) -> {
            return p.get("authority").toString().startsWith("ROLE_");
        }).map((p) -> {
            return p.get("authority").toString().replace("ROLE_", "");
        }).toArray((i) -> {
            return new String[i];
        }));

        authUser.setPermissions(rolePermissions.stream().filter((p) -> {
            return !p.get("authority").toString().startsWith("ROLE_");
        }).map((p) -> {
            return p.get("authority").toString();
        }).toArray((i) -> {
            return new String[i];
        }));

        return authUser;
    }
}
